<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  

   <!-- 百度联盟 -->
   <meta name="baidu_union_verify" content="46e227ad81d5513c6cd8288c18c6ebad">

   <!-- google analytics -->
   <meta name="google-site-verification" content="SzCGQVmA8Mtk40elee-bCGpq2YSCAmEulSNEZHYCkFc" />

   <!--百度站长之家验证-->
   <meta name="baidu-site-verification" content="4woRik4rfk" />

   <meta name="description" content="坑要一个个填，路要一步步走！—— from zhisheng的博客">
   <meta name="keywords" content="Java,架构,后端,服务端,RocketMQ,分布式消息队列,分布式存储,技术博客,HBase,ElasticSearch,Spring,Spring Boot,Spring Boot 2.0,Spring Cloud,Spring MVC,Java EE,前端,HTML,MyBatis,Android,Docker,Mac,Consul,Kafka,Logstash,Kibana,MySQL,Maven,Nginx,Python,RabbitMQ,ActiveMQ,JVM">


  <title>《深入理解 Java 内存模型》读书笔记 | zhisheng的博客</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="前提《深入理解 Java 内存模型》程晓明著，该书在以前看过一遍，现在学的东西越多，感觉那块越重要，于是又再细看一遍，于是便有了下面的读书笔记总结。全书页数虽不多，内容讲得挺深的。细看的话，也是挺花时间的，看完收获绝对挺大的。也建议 Java 开发者都去看看。里面主要有 Java 内存模型的基础、重排序、顺序一致性、Volatile 关键字、锁、final。本文参考书中内容。">
<meta name="keywords" content="Java,JMM">
<meta property="og:type" content="article">
<meta property="og:title" content="《深入理解 Java 内存模型》读书笔记">
<meta property="og:url" content="http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/index.html">
<meta property="og:site_name" content="zhisheng的博客">
<meta property="og:description" content="前提《深入理解 Java 内存模型》程晓明著，该书在以前看过一遍，现在学的东西越多，感觉那块越重要，于是又再细看一遍，于是便有了下面的读书笔记总结。全书页数虽不多，内容讲得挺深的。细看的话，也是挺花时间的，看完收获绝对挺大的。也建议 Java 开发者都去看看。里面主要有 Java 内存模型的基础、重排序、顺序一致性、Volatile 关键字、锁、final。本文参考书中内容。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7l5mj64j31hc0zbh9n.jpg">
<meta property="og:image" content="http://image.54tianzhisheng.cn/blog/180103/C6LG3mGa12.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l9q3hsj30zi0rq41t.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7lh7qm2j30wo0ocq58.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7l4iv44j313e07wmyw.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l6udu0j30uc0c8zls.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7lftsaxj30wg0mcwgz.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l7e9zij312s0titdo.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l7q6paj30fa0aoq33.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l97fx2j30zu0cwdh1.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lcgkgej30tw0ki3zd.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l4zjfkj30zy0owq4r.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7l6dyuaj30sk0mumyg.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7ldkj04j31440paaee.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lefk10j316c0nytd8.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7ld04ffj312s0tq432.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7levu1fj30zm0rgmzu.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l88o2aj312i0l8jt0.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7lak6kvj30xc0qc76l.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lg83wdj313u0ckmz8.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7la65xnj31100lc76r.jpg">
<meta property="og:image" content="https://ws1.sinaimg.cn/large/006tNc79gy1fow7l35uyuj310w0myju1.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l42rbpj31160qa77a.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l8rgjej30ys0r6n05.jpg">
<meta property="og:image" content="https://ws2.sinaimg.cn/large/006tNc79gy1fow7lb5c5kj310y0z4q7w.jpg">
<meta property="og:image" content="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lgmupij311m0sqtcg.jpg">
<meta property="og:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fp3jkmizmpj30o00didgn.jpg">
<meta property="og:updated_time" content="2018-11-22T14:51:58.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="《深入理解 Java 内存模型》读书笔记">
<meta name="twitter:description" content="前提《深入理解 Java 内存模型》程晓明著，该书在以前看过一遍，现在学的东西越多，感觉那块越重要，于是又再细看一遍，于是便有了下面的读书笔记总结。全书页数虽不多，内容讲得挺深的。细看的话，也是挺花时间的，看完收获绝对挺大的。也建议 Java 开发者都去看看。里面主要有 Java 内存模型的基础、重排序、顺序一致性、Volatile 关键字、锁、final。本文参考书中内容。">
<meta name="twitter:image" content="https://ws3.sinaimg.cn/large/006tNc79gy1fow7l5mj64j31hc0zbh9n.jpg">
  
    <link rel="alternative" href="/atom.xml" title="zhisheng的博客" type="application/atom+xml">
  
  
    <link rel="icon" href="/img/favicon.ico">
  
  
      <link rel="stylesheet" href="//cdn.bootcss.com/animate.css/3.5.0/animate.min.css">
  
  <link rel="stylesheet" href="/css/style.css">
  <link rel="stylesheet" href="/font-awesome/css/font-awesome.min.css">
  <link rel="apple-touch-icon" href="/apple-touch-icon.png">
  
  
      <link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
  
  <!-- 加载特效 -->
  <script src="/js/pace.js"></script>
  <link href="/css/pace/pace-theme-flash.css" rel="stylesheet" />
  <script>
      var yiliaConfig = {
          rootUrl: '/',
          fancybox: true,
          animate: true,
          isHome: false,
          isPost: true,
          isArchive: false,
          isTag: false,
          isCategory: false,
          open_in_new: true
      }
  </script>
</head>
<body>
  <div id="container">
    <div class="left-col">
    <div class="overlay"></div>
<div class="intrude-less">
    <header id="header" class="inner">
        <a href="/" class="profilepic">
            
            <img lazy-src="/img/avatar.png" class="js-avatar">
            
        </a>

        <hgroup>
          <h1 class="header-author"><a href="/">zhisheng</a></h1>
        </hgroup>

        
        <p class="header-subtitle">坑要一个个填，路要一步步走！</p>
        
        
        
            <div id="switch-btn" class="switch-btn">
                <div class="icon">
                    <div class="icon-ctn">
                        <div class="icon-wrap icon-house" data-idx="0">
                            <div class="birdhouse"></div>
                            <div class="birdhouse_holes"></div>
                        </div>
                        <div class="icon-wrap icon-ribbon hide" data-idx="1">
                            <div class="ribbon"></div>
                        </div>
                        
                        <div class="icon-wrap icon-link hide" data-idx="2">
                            <div class="loopback_l"></div>
                            <div class="loopback_r"></div>
                        </div>
                        
                        
                        <div class="icon-wrap icon-me hide" data-idx="3">
                            <div class="user"></div>
                            <div class="shoulder"></div>
                        </div>
                        
                    </div>
                    
                </div>
                <div class="tips-box hide">
                    <div class="tips-arrow"></div>
                    <ul class="tips-inner">
                        <li>菜单</li>
                        <li>标签</li>
                        
                        <li>友情链接</li>
                        
                        
                        <li>关于我</li>
                        
                    </ul>
                </div>
            </div>
        

        <div id="switch-area" class="switch-area">
            <div class="switch-wrap">
                <section class="switch-part switch-part1">
                    <nav class="header-menu">
                        <ul>
                        
                            <li><a href="/">主页</a></li>
                        
                            <li><a href="/tags/Flink/">Flink</a></li>
                        
                            <li><a href="/tags/ElasticSearch/">ElasticSearch</a></li>
                        
                            <li><a href="/tags/SpringBoot/">Spring Boot</a></li>
                        
                        </ul>
                    </nav>
                    <nav class="header-nav">
                        <ul class="social">
                            
                                <a class="fl wechat" target="_blank" href="wechat:zhisheng_tian" title="wechat">wechat</a>
                            
                                <a class="fl mail" target="_blank" href="mailto://zhisheng2018@gmail.com" title="mail">mail</a>
                            
                                <a class="fl github" target="_blank" href="https://github.com/zhisheng17/" title="github">github</a>
                            
                                <a class="fl zhihu" target="_blank" href="https://www.zhihu.com/people/tian-zhisheng/activities" title="zhihu">zhihu</a>
                            
                        </ul>
                    </nav>
                </section>
                
                
                <section class="switch-part switch-part2">
                    <div class="widget tagcloud" id="js-tagcloud">
                        <a href="/tags/AJAX/" style="font-size: 10px;">AJAX</a> <a href="/tags/ActiveMQ/" style="font-size: 10px;">ActiveMQ</a> <a href="/tags/Android/" style="font-size: 10px;">Android</a> <a href="/tags/Bootstrap/" style="font-size: 13px;">Bootstrap</a> <a href="/tags/Consul/" style="font-size: 10px;">Consul</a> <a href="/tags/Docker/" style="font-size: 11px;">Docker</a> <a href="/tags/ElasticSearch/" style="font-size: 17px;">ElasticSearch</a> <a href="/tags/Filter过滤器/" style="font-size: 10px;">Filter过滤器</a> <a href="/tags/Flink/" style="font-size: 18px;">Flink</a> <a href="/tags/GO/" style="font-size: 10px;">GO</a> <a href="/tags/Github-Page/" style="font-size: 10px;">Github Page</a> <a href="/tags/Guava/" style="font-size: 10px;">Guava</a> <a href="/tags/HBase/" style="font-size: 10px;">HBase</a> <a href="/tags/Hibernate-JPA/" style="font-size: 11px;">Hibernate JPA</a> <a href="/tags/IO/" style="font-size: 11px;">IO</a> <a href="/tags/JMM/" style="font-size: 10px;">JMM</a> <a href="/tags/JSON/" style="font-size: 10px;">JSON</a> <a href="/tags/JVM/" style="font-size: 11px;">JVM</a> <a href="/tags/Java/" style="font-size: 20px;">Java</a> <a href="/tags/Kafka/" style="font-size: 12px;">Kafka</a> <a href="/tags/Kibana/" style="font-size: 10px;">Kibana</a> <a href="/tags/LogStash/" style="font-size: 10px;">LogStash</a> <a href="/tags/Mac/" style="font-size: 10px;">Mac</a> <a href="/tags/Maven/" style="font-size: 11px;">Maven</a> <a href="/tags/MySQL/" style="font-size: 12px;">MySQL</a> <a href="/tags/Mybatis/" style="font-size: 14px;">Mybatis</a> <a href="/tags/NIO/" style="font-size: 10px;">NIO</a> <a href="/tags/Netty/" style="font-size: 10px;">Netty</a> <a href="/tags/Nginx/" style="font-size: 11px;">Nginx</a> <a href="/tags/Oracle/" style="font-size: 10px;">Oracle</a> <a href="/tags/Pyspider/" style="font-size: 10px;">Pyspider</a> <a href="/tags/Python/" style="font-size: 12px;">Python</a> <a href="/tags/RabbitMQ/" style="font-size: 11px;">RabbitMQ</a> <a href="/tags/Redis/" style="font-size: 11px;">Redis</a> <a href="/tags/RocketMQ/" style="font-size: 13px;">RocketMQ</a> <a href="/tags/Servlet/" style="font-size: 10px;">Servlet</a> <a href="/tags/Spring/" style="font-size: 12px;">Spring</a> <a href="/tags/Spring-MVC/" style="font-size: 15px;">Spring MVC</a> <a href="/tags/SpringBoot/" style="font-size: 19px;">SpringBoot</a> <a href="/tags/SpringCloud/" style="font-size: 10px;">SpringCloud</a> <a href="/tags/SpringMVC/" style="font-size: 13px;">SpringMVC</a> <a href="/tags/String/" style="font-size: 10px;">String</a> <a href="/tags/Velocity/" style="font-size: 10px;">Velocity</a> <a href="/tags/Zookeeper/" style="font-size: 10px;">Zookeeper</a> <a href="/tags/finally/" style="font-size: 10px;">finally</a> <a href="/tags/foreach/" style="font-size: 10px;">foreach</a> <a href="/tags/hexo/" style="font-size: 12px;">hexo</a> <a href="/tags/lombok/" style="font-size: 10px;">lombok</a> <a href="/tags/lua/" style="font-size: 10px;">lua</a> <a href="/tags/yilia/" style="font-size: 11px;">yilia</a> <a href="/tags/书籍/" style="font-size: 10px;">书籍</a> <a href="/tags/分布式锁/" style="font-size: 10px;">分布式锁</a> <a href="/tags/前端/" style="font-size: 12px;">前端</a> <a href="/tags/励志/" style="font-size: 10px;">励志</a> <a href="/tags/博客合集/" style="font-size: 10px;">博客合集</a> <a href="/tags/博客网站/" style="font-size: 10px;">博客网站</a> <a href="/tags/多线程/" style="font-size: 11px;">多线程</a> <a href="/tags/大数据/" style="font-size: 18px;">大数据</a> <a href="/tags/字符串/" style="font-size: 11px;">字符串</a> <a href="/tags/实习圈/" style="font-size: 10px;">实习圈</a> <a href="/tags/循环队列/" style="font-size: 10px;">循环队列</a> <a href="/tags/微服务/" style="font-size: 11px;">微服务</a> <a href="/tags/性能调优工具/" style="font-size: 10px;">性能调优工具</a> <a href="/tags/投资理财/" style="font-size: 10px;">投资理财</a> <a href="/tags/数据库/" style="font-size: 12px;">数据库</a> <a href="/tags/数据结构/" style="font-size: 13px;">数据结构</a> <a href="/tags/文件/" style="font-size: 10px;">文件</a> <a href="/tags/旋转词/" style="font-size: 10px;">旋转词</a> <a href="/tags/流式计算/" style="font-size: 18px;">流式计算</a> <a href="/tags/流控/" style="font-size: 10px;">流控</a> <a href="/tags/爬虫/" style="font-size: 12px;">爬虫</a> <a href="/tags/算法/" style="font-size: 12px;">算法</a> <a href="/tags/类加载机制/" style="font-size: 10px;">类加载机制</a> <a href="/tags/线程池/" style="font-size: 10px;">线程池</a> <a href="/tags/编码/" style="font-size: 10px;">编码</a> <a href="/tags/表达式/" style="font-size: 10px;">表达式</a> <a href="/tags/邮件发送/" style="font-size: 10px;">邮件发送</a> <a href="/tags/随笔/" style="font-size: 16px;">随笔</a> <a href="/tags/面经/" style="font-size: 16px;">面经</a>
                    </div>
                </section>
                
                
                
                <section class="switch-part switch-part3">
                    <div id="js-friends">
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.csdn.net/tzs_1041218129">CSDN博客</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="https://juejin.im/user/57510b82128fe10056ca70fc">掘金</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://www.iocoder.cn/?vip">芋道源码</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://fengzhaofeng.net/">冯兆峯</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://www.codedata.cn/">CodeData</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="https://investguider.com/">美股指南</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://www.songyawei.cn/">Linux运维工程师</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://www.carlzone.cn/">carl.zhao</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://dongkelun.com/">伦少的博客</a>
                    
                      <a target="_blank" class="main-nav-link switch-friends-link" href="http://www.jiangxinlingdu.com/">匠心零度</a>
                    
                    </div>
                </section>
                

                
                
                <section class="switch-part switch-part4">
                
                    <div id="js-aboutme">17.08 ~ 18.05 永辉云创基础架构组实习     2018.06 大学毕业   2018.06 ～ 现在  另一家公司各种折腾!</div>
                </section>
                
            </div>
        </div>
        <div>
            <img src="/img/wx.jpg"  alt="zhisheng" />
        </div>
    </header>
</div>
    </div>
    <div class="mid-col">
      <nav id="mobile-nav">
      <div class="overlay">
          <div class="slider-trigger"></div>
          <h1 class="header-author js-mobile-header hide"><a href="/" title="Me">zhisheng</a></h1>
      </div>
    <div class="intrude-less">
        <header id="header" class="inner">
            <a href="/" class="profilepic">
                
                    <img lazy-src="/img/avatar.png" class="js-avatar">
                
            </a>
            <hgroup>
              <h1 class="header-author"><a href="/" title="Me">zhisheng</a></h1>
            </hgroup>
            
            <p class="header-subtitle">坑要一个个填，路要一步步走！</p>
            
            <nav class="header-menu">
                <ul>
                
                    <li><a href="/">主页</a></li>
                
                    <li><a href="/tags/Flink/">Flink</a></li>
                
                    <li><a href="/tags/ElasticSearch/">ElasticSearch</a></li>
                
                    <li><a href="/tags/SpringBoot/">Spring Boot</a></li>
                
                <div class="clearfix"></div>
                </ul>
            </nav>
            <nav class="header-nav">
                <div class="social">
                    
                        <a class="wechat" target="_blank" href="wechat:zhisheng_tian" title="wechat">wechat</a>
                    
                        <a class="mail" target="_blank" href="mailto://zhisheng2018@gmail.com" title="mail">mail</a>
                    
                        <a class="github" target="_blank" href="https://github.com/zhisheng17/" title="github">github</a>
                    
                        <a class="zhihu" target="_blank" href="https://www.zhihu.com/people/tian-zhisheng/activities" title="zhihu">zhihu</a>
                    
                </div>
            </nav>
        </header>                
    </div>
</nav>
      <div class="body-wrap"><article id="post-Java-Memory-Model" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/2018/02/28/Java-Memory-Model/" class="article-date">
      <time datetime="2018-02-27T16:00:00.000Z" itemprop="datePublished">2018-02-28</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      《深入理解 Java 内存模型》读书笔记
    </h1>
  

      </header>
      
      <div class="article-info article-info-post">
        

        
    <div class="article-tag tagcloud">
        <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JMM/">JMM</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li></ul>
    </div>

        <div class="clearfix"></div>
      </div>
      
    
    <div class="article-entry" itemprop="articleBody">
      
          
        <p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7l5mj64j31hc0zbh9n.jpg" alt="fairyland-canyon-1632749_1920"></p>
<h3 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h3><p>《深入理解 Java 内存模型》程晓明著，该书在以前看过一遍，现在学的东西越多，感觉那块越重要，于是又再细看一遍，于是便有了下面的读书笔记总结。全书页数虽不多，内容讲得挺深的。细看的话，也是挺花时间的，看完收获绝对挺大的。也建议 Java 开发者都去看看。里面主要有 Java 内存模型的基础、重排序、顺序一致性、Volatile 关键字、锁、final。本文参考书中内容。</p>
<a id="more"></a>
<h3 id="关注我"><a href="#关注我" class="headerlink" title="关注我"></a>关注我</h3><p>如果你想查看这本书可以关注我的公众号: <strong>zhisheng</strong> ，然后里面回复关键字 <strong>JMM</strong> 可以查看我分享的百度云链接。</p>
<p><img src="http://image.54tianzhisheng.cn/blog/180103/C6LG3mGa12.jpg" alt="mark"></p>
<p>转载请务必注明原创地址为：<a href="http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/">http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/</a></p>
<h3 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h3><h4 id="并发编程的模型分类"><a href="#并发编程的模型分类" class="headerlink" title="并发编程的模型分类"></a>并发编程的模型分类</h4><p>在并发编程需要处理的两个关键问题是：<strong>线程之间如何通信</strong> 和 <strong>线程之间如何同步</strong>。</p>
<h5 id="通信"><a href="#通信" class="headerlink" title="通信"></a>通信</h5><p><strong>通信</strong> 是指线程之间以何种机制来交换信息。在命令式编程中，线程之间的通信机制有两种：<strong>共享内存</strong> 和 <strong>消息传递</strong>。</p>
<p>在<strong>共享内存</strong>的并发模型里，线程之间共享程序的公共状态，线程之间通过写-读内存中的<strong>公共状态</strong>来<strong>隐式</strong>进行通信。</p>
<p>在<strong>消息传递</strong>的并发模型里，线程之间没有公共状态，线程之间必须通过明确的<strong>发送消息</strong>来<strong>显式</strong>进行通信。</p>
<h5 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h5><p><strong>同步</strong> 是指程序用于控制不同线程之间操作发生相对顺序的机制。</p>
<p>在<strong>共享内存</strong>的并发模型里，同步是<strong>显式</strong>进行的。程序员必须显式指定某个方法或某段代码需要在线程之间<strong>互斥执行</strong>。</p>
<p>在<strong>消息传递</strong>的并发模型里，由于消息的发送必须在消息的接收之前，因此同步是<strong>隐式</strong>进行的。</p>
<p>Java 的并发采用的是共享内存模型，Java 线程之间的通信总是隐式进行，整个通信过程对程序员完全透明。</p>
<h4 id="Java-内存模型的抽象"><a href="#Java-内存模型的抽象" class="headerlink" title="Java 内存模型的抽象"></a>Java 内存模型的抽象</h4><p>在 Java 中，所有实例域、静态域 和 数组元素存储在堆内存中，堆内存在线程之间共享。局部变量、方法定义参数 和 异常处理器参数 不会在线程之间共享，它们不会有内存可见性问题，也不受内存模型的影响。</p>
<p>Java 线程之间的通信由 Java 内存模型（JMM）控制。JMM 决定了一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看，JMM 定义了线程与主内存之间的抽象关系：线程之间的共享变量存储在主内存中，每一个线程都有一个自己私有的本地内存，本地内存中存储了该变量以读／写共享变量的副本。本地内存是 JMM 的一个抽象概念，并不真实存在。</p>
<p>JMM 抽象示意图：</p>
<p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l9q3hsj30zi0rq41t.jpg" alt="jmm"></p>
<p>从上图来看，如果线程 A 和线程 B 要通信的话，要如下两个步骤：</p>
<p>1、线程 A 需要将本地内存 A 中的共享变量副本刷新到主内存去</p>
<p>2、线程 B 去主内存读取线程 A 之前已更新过的共享变量</p>
<p>步骤示意图：</p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7lh7qm2j30wo0ocq58.jpg" alt="tongxin"></p>
<p>举个例子：</p>
<p>本地内存 A 和 B 有主内存共享变量 X 的副本。假设一开始时，这三个内存中 X 的值都是 0。线程 A 正执行时，把更新后的 X 值（假设为 1）临时存放在自己的本地内存 A 中。当线程 A 和 B 需要通信时，线程 A 首先会把自己本地内存 A 中修改后的 X 值刷新到主内存去，此时主内存中的 X 值变为了 1。随后，线程 B 到主内存中读取线程 A 更新后的共享变量 X 的值，此时线程 B 的本地内存的 X 值也变成了 1。</p>
<p>整体来看，这两个步骤实质上是线程 A 再向线程 B 发送消息，而这个通信过程必须经过主内存。JMM 通过控制主内存与每个线程的本地内存之间的交互，来为 Java 程序员提供内存可见性保证。</p>
<h3 id="重排序"><a href="#重排序" class="headerlink" title="重排序"></a>重排序</h3><p>在执行程序时为了提高性能，编译器和处理器常常会对指令做重排序。重排序分三类：</p>
<p>1、<strong>编译器优化的重排序</strong>。编译器在不改变单线程程序语义的前提下，可以重新安排语句的执行顺序。</p>
<p>2、<strong>指令级并行的重排序</strong>。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性，处理器可以改变语句对应机器指令的执行顺序。</p>
<p>3、<strong>内存系统的重排序</strong>。由于处理器使用缓存和读／写缓冲区，这使得加载和存储操作看上去可能是在乱序执行。</p>
<p>从 Java 源代码到最终实际执行的指令序列，会分别经历下面三种重排序：</p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7l4iv44j313e07wmyw.jpg" alt="sort-again"></p>
<p>上面的这些重排序都可能导致多线程程序出现内存可见性问题。对于编译器，JMM 的编译器重排序规则会禁止特定类型的编译器重排序（不是所有的编译器重排序都要禁止）。对于处理器重排序，JMM 的处理器重排序规则会要求 Java 编译器在生成指令序列时，插入特定类型的内存屏障指令，通过内存屏障指令来禁止特定类型的处理器重排序（不是所有的处理器重排序都要禁止）。</p>
<p>JMM 属于语言级的内存模型，它确保在不同的编译器和不同的处理器平台之上，通过禁止特定类型的编译器重排序和处理器重排序，为程序员提供一致的内存可见性保证。</p>
<h4 id="处理器重排序"><a href="#处理器重排序" class="headerlink" title="处理器重排序"></a>处理器重排序</h4><p>现代的处理器使用<strong>写缓冲区</strong>来临时保存向内存写入的数据。写缓冲区可以保证指令流水线持续运行，它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时，通过以批处理的方式刷新写缓冲区，以及合并写缓冲区中对同一内存地址的多次写，可以减少对内存总线的占用。虽然写缓冲区有这么多好处，但每个处理器上的写缓冲区，仅仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响：处理器对内存的读/写操作的执行顺序，不一定与内存实际发生的读/写操作顺序一致！</p>
<p>举个例子：</p>
<p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l6udu0j30uc0c8zls.jpg" alt="example1"></p>
<p>假设处理器A和处理器B按程序的顺序并行执行内存访问，最终却可能得到 x = y = 0。具体的原因如下图所示：</p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7lftsaxj30wg0mcwgz.jpg" alt="exam1-ans"></p>
<p>处理器 A 和 B 同时把共享变量写入在写缓冲区中（A1、B1），然后再从内存中读取另一个共享变量（A2、B2），最后才把自己写缓冲区中保存的脏数据刷新到内存中（A3、B3）。当以这种时序执行时，程序就可以得到 x = y = 0 的结果。</p>
<p>从内存操作实际发生的顺序来看，直到处理器 A 执行 A3 来刷新自己的写缓存区，写操作 A1 才算真正执行了。虽然处理器 A 执行内存操作的顺序为：A1 -&gt; A2，但内存操作实际发生的顺序却是：A2 -&gt; A1。此时，处理器 A 的内存操作顺序被重排序了。</p>
<p>这里的关键是，由于<strong>写缓冲区仅对自己的处理器可见，它会导致处理器执行内存操作的顺序可能会与内存实际的操作执行顺序不一致</strong>。由于现代的处理器都会使用写缓冲区，因此现代的处理器都会允许对写-读操作重排序。</p>
<h4 id="内存屏障指令"><a href="#内存屏障指令" class="headerlink" title="内存屏障指令"></a>内存屏障指令</h4><p>为了保证内存可见性，Java 编译器在生成指令序列的适当位置会插入<strong>内存屏障指令</strong>来禁止特定类型的处理器重排序。JMM 把内存屏障指令分为下列四类：</p>
<table>
<thead>
<tr>
<th>屏障类型</th>
<th style="text-align:center">指令示例</th>
<th style="text-align:center">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>LoadLoad Barriers</td>
<td style="text-align:center">Load1; LoadLoad; Load2</td>
<td style="text-align:center">确保 Load1 数据的装载，之前于 Load2 及所有后续装载指令的装载。</td>
</tr>
<tr>
<td>StoreStore Barriers</td>
<td style="text-align:center">Store1; StoreStore; Store2</td>
<td style="text-align:center">确保 Store1 数据对其他处理器可见（刷新到内存），之前于 Store2 及所有后续存储指令的存储。</td>
</tr>
<tr>
<td>LoadStore Barriers</td>
<td style="text-align:center">Load1; LoadStore; Store2</td>
<td style="text-align:center">确保 Load1 数据装载，之前于 Store2 及所有后续的存储指令刷新到内存。</td>
</tr>
<tr>
<td>StoreLoad Barriers</td>
<td style="text-align:center">Store1; StoreLoad; Load2</td>
<td style="text-align:center">确保 Store1 数据对其他处理器变得可见（指刷新到内存），之前于 Load2 及所有后续装载指令的装载。<strong>StoreLoadBarriers 会使该屏障之前的所有内存访问指令（存储和装载指令）完成之后，才执行该屏障之后的内存访问指令</strong>。</td>
</tr>
</tbody>
</table>
<h4 id="happens-before"><a href="#happens-before" class="headerlink" title="happens-before"></a>happens-before</h4><p>JSR-133 内存模型使用 happens-before 的概念来阐述操作之间的内存可见性。在 JMM 中，如果<strong>一个操作执行的结果需要对另一个操作可见</strong>，那么这两个操作之间必须要存在 happens-before 关系。这里提到的两个操作既可以是在一个线程之内，也可以是在不同线程之间。</p>
<p>与程序员密切相关的 happens-before 规则如下：</p>
<ul>
<li>程序顺序规则：一个线程中的每个操作，happens-before 于该线程中的任意后续操作。</li>
<li>监视器锁规则：对一个监视器的解锁，happens-before 于随后对这个监视器的加锁。</li>
<li>volatile 变量规则：对一个 volatile 域的写，happens-before 于任意后续对这个 volatile 域的读。</li>
<li>传递性：如果  A happens-before B，且 B happens-before C，那么 A happens-before C。</li>
</ul>
<p>注意，两个操作之间具有 happens-before 关系，并不意味着前一个操作必须要在后一个操作之前执行！happens-before 仅仅要求前一个操作（执行的结果）对后一个操作可见，且前一个操作按顺序排在第二个操作之前（the first is visible to and ordered before the second）。</p>
<p>happens-before 与 JMM 的关系如下图所示：</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l7e9zij312s0titdo.jpg" alt="happens-before-jmm"></p>
<p>如上图所示，一个 happens-before 规则对应于一个或多个编译器和处理器重排序规则。</p>
<h4 id="数据依赖性"><a href="#数据依赖性" class="headerlink" title="数据依赖性"></a>数据依赖性</h4><p>如果两个操作访问同一个变量，且这两个操作中有一个为写操作，此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型：</p>
<table>
<thead>
<tr>
<th style="text-align:center">名称</th>
<th style="text-align:center">代码示例</th>
<th style="text-align:center">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">写后读</td>
<td style="text-align:center">a = 1;   b = a;</td>
<td style="text-align:center">写一个变量之后，再读这个位置。</td>
</tr>
<tr>
<td style="text-align:center">写后写</td>
<td style="text-align:center">a = 1;  a = 2;</td>
<td style="text-align:center">写一个变量之后，再写这个变量。</td>
</tr>
<tr>
<td style="text-align:center">读后写</td>
<td style="text-align:center">a = b;  b = 1;</td>
<td style="text-align:center">读一个变量之后，再写这个变量。</td>
</tr>
</tbody>
</table>
<p>上面三种情况，只要重排序两个操作的执行顺序，程序的执行结果将会被改变。</p>
<p>前面提到过，编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时，会遵守数据依赖性，编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。</p>
<p>注意，这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作，不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。</p>
<h4 id="as-if-serial-语义"><a href="#as-if-serial-语义" class="headerlink" title="as-if-serial 语义"></a>as-if-serial 语义</h4><p>as-if-serial 语义的意思指：<strong>不管怎么重排序</strong>（编译器和处理器为了提高并行度），（单线程）<strong>程序的执行结果不能被改变</strong>。编译器，runtime 和处理器都必须遵守 as-if-serial 语义。</p>
<p>为了遵守 as-if-serial 编译器和处理器不会对存在数据依赖关系的操作做重排序，因为这种重排序会改变执行结果。但是如果操作之间没有数据依赖关系，这些操作就可能被编译器和处理器重排序。</p>
<p>举个例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">double pi = 3.14;     //A</span><br><span class="line">double r  = 1.0;       //B</span><br><span class="line">double area = pi * r * r;     //C</span><br></pre></td></tr></table></figure>
<p>上面三个操作的数据依赖关系如下图所示：</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l7q6paj30fa0aoq33.jpg" alt="abc"></p>
<p>如上图所示，A 和 C 之间存在数据依赖关系，同时 B 和 C 之间也存在数据依赖关系。因此在最终执行的指令序列中，C 不能被重排序到 A 和 B 的前面（C 排到 A 和 B 的前面，程序的结果将会被改变）。但 A 和 B 之间没有数据依赖关系，编译器和处理器可以重排序 A 和 B 之间的执行顺序。下图是该程序的两种执行顺序：</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l97fx2j30zu0cwdh1.jpg" alt="2018-02-27_16-52-44"></p>
<p>在计算机中，软件技术和硬件技术有一个共同的目标：在不改变程序执行结果的前提下，尽可能的开发并行度。编译器和处理器遵从这一目标，从 happens-before 的定义我们可以看出，JMM 同样遵从这一目标。</p>
<h4 id="重排序对多线程的影响"><a href="#重排序对多线程的影响" class="headerlink" title="重排序对多线程的影响"></a>重排序对多线程的影响</h4><p>举例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>&#123;</span><br><span class="line">  <span class="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">boolean</span> flag = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    a = <span class="number">1</span>;			<span class="comment">//1</span></span><br><span class="line">    flag = <span class="keyword">true</span>;	<span class="comment">//2</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">read</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(flag) &#123;			<span class="comment">//3</span></span><br><span class="line">      <span class="keyword">int</span> i = a * a;	<span class="comment">//4</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>由于操作 1 和 2 没有数据依赖关系，编译器和处理器可以对这两个操作重排序；操作 3 和操作 4 没有数据依赖关系，编译器和处理器也可以对这两个操作重排序。</p>
<p>1、当操作 1 和操作 2 重排序时，可能会产生什么效果？</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lcgkgej30tw0ki3zd.jpg" alt="sort12"></p>
<p>如上图所示，操作 1 和操作 2 做了重排序。程序执行时，线程 A 首先写标记变量 flag，随后线程 B 读这个变量。由于条件判断为真，线程 B 将读取变量 a。此时，变量 a 还根本没有被线程 A 写入，在这里多线程程序的语义被重排序破坏了！</p>
<p>2、当操作 3 和操作 4 重排序时会产生什么效果（借助这个重排序，可以顺便说明控制依赖性）。</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7l4zjfkj30zy0owq4r.jpg" alt="sort34"></p>
<p>在程序中，操作 3 和操作 4 存在<strong>控制依赖关系</strong>。当代码中存在控制依赖性时，<strong>会影响指令序列执行的并行度</strong>。为此，编译器和处理器会采用<strong>猜测</strong>（Speculation）执行来克服控制相关性对并行度的影响。以处理器的猜测执行为例，执行线程 B 的处理器可以提前读取并计算 a * a，然后把计算结果临时保存到一个名为重排序缓冲（reorder buffer ROB）的硬件缓存中。当接下来操作 3 的条件判断为真时，就把该计算结果写入变量 i 中。</p>
<p>从图中我们可以看出，猜测执行实质上对操作3和4做了重排序。重排序在这里破坏了多线程程序的语义！</p>
<p>在单线程程序中，对存在控制依赖的操作重排序，不会改变执行结果（这也是 as-if-serial 语义允许对存在控制依赖的操作做重排序的原因）；但在多线程程序中，对存在控制依赖的操作重排序，可能会改变程序的执行结果。</p>
<h3 id="顺序一致性"><a href="#顺序一致性" class="headerlink" title="顺序一致性"></a>顺序一致性</h3><h4 id="顺序一致性内存模型"><a href="#顺序一致性内存模型" class="headerlink" title="顺序一致性内存模型"></a>顺序一致性内存模型</h4><p>顺序一致性内存模型有两大特性：</p>
<ul>
<li>一个线程中的所有操作必须按照程序的顺序来执行。</li>
<li>（不管程序是否同步）所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中，每个操作都必须原子执行且立刻对所有线程可见。</li>
</ul>
<p>顺序一致性内存模型为程序员提供的视图如下：</p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7l6dyuaj30sk0mumyg.jpg" alt="2018-02-27_17-55-09"></p>
<p>在概念上，顺序一致性模型有一个单一的全局内存，这个内存通过一个左右摆动的开关可以连接到任意一个线程，同时每一个线程必须按照程序的顺序来执行内存读/写操作。从上面的示意图我们可以看出，在任意时间点最多只能有一个线程可以连接到内存。当多个线程并发执行时，图中的开关装置能把所有线程的所有内存读/写操作串行化。</p>
<p>举个例子：</p>
<p>假设有两个线程 A 和 B 并发执行。其中 A 线程有三个操作，它们在程序中的顺序是：A1 -&gt; A2 -&gt; A3。B 线程也有三个操作，它们在程序中的顺序是：B1 -&gt; B2 -&gt; B3。</p>
<p>假设这两个线程使用监视器锁来正确同步：A 线程的三个操作执行后释放监视器锁，随后 B 线程获取同一个监视器锁。那么程序在顺序一致性模型中的执行效果将如下图所示：</p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7ldkj04j31440paaee.jpg" alt="2018-02-27_18-01-51"></p>
<p>现在我们再假设这两个线程没有做同步，下面是这个未同步程序在顺序一致性模型中的执行示意图：</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lefk10j316c0nytd8.jpg" alt="2018-02-27_18-04-20"></p>
<p>未同步程序在顺序一致性模型中虽然整体执行顺序是无序的，但所有线程都只能看到一个一致的整体执行顺序。以上图为例，线程 A 和 B 看到的执行顺序都是：B1 -&gt; A1 -&gt; A2 -&gt; B2 -&gt; A3 -&gt; B3。之所以能得到这个保证是因为顺序一致性内存模型中的每个操作必须立即对任意线程可见。</p>
<p>但是，在 JMM 中就没有这个保证。未同步程序在 JMM 中不但整体的执行顺序是无序的，而且所有线程看到的操作执行顺序也可能不一致。比如，在当前线程把写过的数据缓存在本地内存中，在还没有刷新到主内存之前，这个写操作仅对当前线程可见；从其他线程的角度来观察，会认为这个写操作根本还没有被当前线程执行。只有当前线程把本地内存中写过的数据刷新到主内存之后，这个写操作才能对其他线程可见。在这种情况下，当前线程和其它线程看到的操作执行顺序将不一致。</p>
<h4 id="同步程序的顺序一致性效果"><a href="#同步程序的顺序一致性效果" class="headerlink" title="同步程序的顺序一致性效果"></a>同步程序的顺序一致性效果</h4><p>下面我们对前面的示例程序用锁来同步，看看正确同步的程序如何具有顺序一致性。</p>
<p>请看下面的示例代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">demo</span> </span>&#123;</span><br><span class="line">  <span class="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">boolean</span> flag = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">()</span> </span>&#123;	<span class="comment">//获取锁</span></span><br><span class="line">    a = <span class="number">1</span>;</span><br><span class="line">    flag = <span class="keyword">true</span>;</span><br><span class="line">  &#125;										<span class="comment">//释放锁</span></span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">read</span><span class="params">()</span> </span>&#123;		<span class="comment">//获取锁</span></span><br><span class="line">    <span class="keyword">if</span>(flag) &#123;</span><br><span class="line">      <span class="keyword">int</span> i = a;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;										<span class="comment">//释放锁</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上面示例代码中，假设 A 线程执行 write() 方法后，B 线程执行 reade() 方法。这是一个正确同步的多线程程序。根据JMM规范，该程序的执行结果将与该程序在顺序一致性模型中的执行结果相同。下面是该程序在两个内存模型中的执行时序对比图：</p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7ld04ffj312s0tq432.jpg" alt="2018-02-27_22-01-59"></p>
<p>在顺序一致性模型中，所有操作完全按程序的顺序执行。而在 JMM 中，临界区内的代码可以重排序（但 JMM 不允许临界区内的代码“逸出”到临界区之外，那样会破坏监视器的语义）。JMM 会在退出临界区和进入临界区这两个关键时间点做一些特别处理，使得线程在这两个时间点具有与顺序一致性模型相同的内存视图。虽然线程 A 在临界区内做了重排序，但由于监视器的互斥执行的特性，这里的线程 B 根本无法“观察”到线程 A 在临界区内的重排序。这种重排序既提高了执行效率，又没有改变程序的执行结果。</p>
<p>从这里我们可以看到 JMM 在具体实现上的基本方针：在不改变（正确同步的）程序执行结果的前提下，尽可能的为编译器和处理器的优化打开方便之门。</p>
<h4 id="未同步程序的执行特性"><a href="#未同步程序的执行特性" class="headerlink" title="未同步程序的执行特性"></a>未同步程序的执行特性</h4><p>未同步程序在 JMM 中的执行时，整体上是无序的，其执行结果无法预知。未同步程序在两个模型中的执行特性有下面几个差异：</p>
<ol>
<li>顺序一致性模型保证单线程内的操作会按程序的顺序执行，而 JMM  不保证单线程内的操作会按程序的顺序执行（比如上面正确同步的多线程程序在临界区内的重排序）。</li>
<li>顺序一致性模型保证所有线程只能看到一致的操作执行顺序，而 JMM 不保证所有线程能看到一致的操作执行顺序。</li>
<li><strong>JMM 不保证对 64 位的 long 型和 double 型变量的读/写操作具有原子性</strong>，而<strong>顺序一致性模型保证对所有的内存读/写操作都具有原子</strong> 。</li>
</ol>
<p>第三个差异与处理器总线的工作机制密切相关。在计算机中，数据通过总线在处理器和内存之间传递。每次处理器和内存之间的数据传递都是通过<strong>总线事务</strong>来完成的。总线事务包括读事务和写事务。读事务从内存传送数据到处理器，写事务从处理器传递数据到内存，每个事务会读／写内存中一个或多个物理上连续的字。总线会同步试图并发使用总线的事务。在一个处理器执行总线事务期间，总线会禁止其它所有的处理器和 I／O 设备执行内存的读／写。</p>
<p>总线的工作机制：</p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fow7levu1fj30zm0rgmzu.jpg" alt="2018-02-27_22-53-53"></p>
<p>如上图所示，假设处理器 A、B、和 C  同时向总线发起总线事务，这时总线仲裁会对竞争作出裁决，假设总线在仲裁后判定处理器 A 在竞争中获胜（总线仲裁会确保所有处理器都能公平的访问内存）。此时处理器 A 继续它的总线事务，而其它两个处理器则要等待处理器 A 的总线事务完成后才能开始再次执行内存访问。假设在处理器 A 执行总线事务期间（不管这个总线事务是读事务还是写事务），处理器 D 向总线发起了总线事务，此时处理器 D 的这个请求会被总线禁止。</p>
<p>总线的这些工作机制可以<strong>把所有处理器对内存的访问以串行化的方式来执行</strong>；<strong>在任意时间点，最多只能有一个处理器能访问内存</strong>。这个特性<strong>确保了单个总线事务之中的内存读/写操作具有原子性</strong>。</p>
<p>在一些 32 位的处理器上，如果要求对 64 位数据的写操作具有原子性，会有比较大的开销。为了照顾这种处理器，Java 语言规范鼓励但不强求 JVM 对 64 位的 long 型变量和 double 型变量的写具有原子性。当 JVM 在这种处理器上运行时，会把一个 64 位 long/ double 型变量的写操作拆分为两个 32 位的写操作来执行。这两个 32 位的写操作可能会被分配到不同的总线事务中执行，此时对这个 64 位变量的写将不具有原子性。</p>
<p>当单个内存操作不具有原子性，将可能会产生意想不到后果。请看下面示意图：</p>
<p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l88o2aj312i0l8jt0.jpg" alt="2018-02-27_23-06-59"></p>
<p>如上图所示，假设处理器 A 写一个 long 型变量，同时处理器 B 要读这个 long 型变量。处理器 A 中 64 位的写操作被拆分为两个 32 位的写操作，且这两个 32 位的写操作被分配到不同的写事务中执行。同时处理器 B 中 64 位的读操作被分配到单个的读事务中执行。当处理器 A 和 B 按上图的时序来执行时，处理器 B 将看到仅仅被处理器 A “写了一半“的无效值。</p>
<p>注意，在 JSR -133 之前的旧内存模型中，一个 64 位 long/ double 型变量的<strong>读/写</strong>操作可以被拆分为两个 32 位的读/写操作来执行。从 JSR -133 内存模型开始（即从JDK5开始），仅仅只允许把一个 64 位 long/ double 型变量的<strong>写</strong>操作拆分为两个 32 位的写操作来执行，<strong>任意的读操作在JSR -133中都必须具有原子性</strong>（即任意读操作必须要在单个读事务中执行）。</p>
<h3 id="Volatile"><a href="#Volatile" class="headerlink" title="Volatile"></a>Volatile</h3><h4 id="Volatile-特性"><a href="#Volatile-特性" class="headerlink" title="Volatile 特性"></a>Volatile 特性</h4><p>举个例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">VolatileTest</span> </span>&#123;</span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">long</span> a = <span class="number">1L</span>; 		<span class="comment">// 使用 volatile 声明 64 位的 long 型</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(<span class="keyword">long</span> l)</span> </span>&#123;</span><br><span class="line">        a = l;                  <span class="comment">//单个 volatile 变量的写</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> a;               <span class="comment">//单个 volatile 变量的读</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getAndIncreament</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        a++;                    <span class="comment">// 复合（多个） volatile 变量的读 /写</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>假设有多个线程分别调用上面程序的三个方法，这个程序在语义上和下面程序等价：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">VolatileTest</span> </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> a = <span class="number">1L</span>; 				<span class="comment">// 64 位的 long 型普通变量</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(<span class="keyword">long</span> l)</span> </span>&#123;	<span class="comment">//对单个普通变量的写用同一个锁同步</span></span><br><span class="line">        a = l;                </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">long</span> <span class="title">get</span><span class="params">()</span> </span>&#123;		<span class="comment">//对单个普通变量的读用同一个锁同步</span></span><br><span class="line">        <span class="keyword">return</span> a;           </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getAndIncreament</span><span class="params">()</span> </span>&#123;		<span class="comment">//普通方法调用</span></span><br><span class="line">      	<span class="keyword">long</span> temp = get();					<span class="comment">//调用已同步的读方法</span></span><br><span class="line">        temp += <span class="number">1L</span>;							<span class="comment">//普通写操作							</span></span><br><span class="line">      	set(temp);							<span class="comment">//调用已同步的写方法</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如上面示例程序所示，对一个 volatile 变量的单个读/写操作，与对一个普通变量的读/写操作使用同一个锁来同步，它们之间的执行效果相同。</p>
<p>锁的 happens-before 规则保证释放锁和获取锁的两个线程之间的内存可见性，这意味着<strong>对一个 volatile 变量的读，总是能看到（任意线程）对这个 volatile 变量最后的写入</strong>。</p>
<p>锁的语义决定了临界区代码的执行具有原子性。这意味着即使是 64 位的 long 型和 double 型变量，只要它是 volatile变量，对该变量的读写就将具有原子性。<strong>如果是多个 volatile 操作或类似于 volatile++ 这种复合操作，这些操作整体上不具有原子性</strong>。</p>
<p>简而言之，volatile 变量自身具有下列特性：</p>
<ul>
<li>可见性。对一个 volatile 变量的读，总是能看到（任意线程）对这个 volatile 变量最后的写入。</li>
<li>原子性：对任意单个 volatile 变量的读/写具有原子性，但类似于 volatile++ 这种复合操作不具有原子性。</li>
</ul>
<h4 id="volatile-写-读的内存定义"><a href="#volatile-写-读的内存定义" class="headerlink" title="volatile 写-读的内存定义"></a>volatile 写-读的内存定义</h4><ul>
<li>当<strong>写</strong>一个 volatile 变量时，JMM 会把该线程对应的本地内存中的共享变量值刷新到主内存。</li>
<li>当<strong>读</strong>一个 volatile 变量时，JMM 会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。</li>
</ul>
<p>假设上面的程序 flag 变量用 volatile 修饰</p>
<p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7lak6kvj30xc0qc76l.jpg" alt="2018-02-28_10-13-39"></p>
<h4 id="volatile-内存语义的实现"><a href="#volatile-内存语义的实现" class="headerlink" title="volatile 内存语义的实现"></a>volatile 内存语义的实现</h4><p>下面是 JMM 针对编译器制定的 volatile 重排序规则表：</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lg83wdj313u0ckmz8.jpg" alt="2018-02-28_10-26-43"></p>
<p>为了实现 volatile 的内存语义，编译器在生成字节码时，会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。</p>
<p>下面是基于保守策略的 JMM 内存屏障插入策略：</p>
<ul>
<li>在每个 volatile 写操作的前面插入一个 StoreStore 屏障。</li>
<li>在每个 volatile 写操作的后面插入一个 StoreLoad 屏障。</li>
<li>在每个 volatile 读操作的后面插入一个 LoadLoad 屏障。</li>
<li>在每个 volatile 读操作的后面插入一个 LoadStore 屏障。</li>
</ul>
<p>下面是保守策略下，volatile 写操作 插入内存屏障后生成的指令序列示意图：</p>
<p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7la65xnj31100lc76r.jpg" alt="2018-02-28_10-31-14"></p>
<p>下面是在保守策略下，volatile 读操作 插入内存屏障后生成的指令序列示意图：</p>
<p><img src="https://ws1.sinaimg.cn/large/006tNc79gy1fow7l35uyuj310w0myju1.jpg" alt="2018-02-28_10-38-12"></p>
<p>上述 volatile 写操作和 volatile 读操作的内存屏障插入策略非常保守。在实际执行时，只要不改变 volatile 写-读的内存语义，编译器可以根据具体情况省略不必要的屏障。</p>
<h3 id="锁"><a href="#锁" class="headerlink" title="锁"></a>锁</h3><h4 id="锁释放和获取的内存语义"><a href="#锁释放和获取的内存语义" class="headerlink" title="锁释放和获取的内存语义"></a>锁释放和获取的内存语义</h4><p>当线程释放锁时，JMM 会把该线程对应的本地内存中的共享变量刷新到主内存中。</p>
<p>当线程获取锁时，JMM 会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须要从主内存中去读取共享变量。</p>
<h4 id="锁内存语义的实现"><a href="#锁内存语义的实现" class="headerlink" title="锁内存语义的实现"></a>锁内存语义的实现</h4><p>借助 ReentrantLock 来讲解，PS： 后面专门讲下这块（ReentrantLock、Synchronized、公平锁、非公平锁、AQS等），可以看看大明哥的博客：<a href="">http://cmsblogs.com/?p=2210</a></p>
<h4 id="concurrent-包的实现"><a href="#concurrent-包的实现" class="headerlink" title="concurrent 包的实现"></a>concurrent 包的实现</h4><p>如果我们仔细分析 concurrent 包的源代码实现，会发现一个通用化的实现模式：</p>
<ol>
<li>首先，声明共享变量为 volatile；</li>
<li>然后，使用 CAS 的原子条件更新来实现线程之间的同步；</li>
<li>同时，配合以 volatile 的读/写和 CAS 所具有的 volatile 读和写的内存语义来实现线程之间的通信。</li>
</ol>
<p>AQS，非阻塞数据结构和原子变量类（java.util.concurrent.atomic 包中的类），这些 concurrent 包中的基础类都是使用这种模式来实现的，而 concurrent 包中的高层类又是依赖于这些基础类来实现的。从整体来看，concurrent 包的实现示意图如下：</p>
<p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l42rbpj31160qa77a.jpg" alt="2018-02-28_14-58-32"></p>
<h3 id="final"><a href="#final" class="headerlink" title="final"></a>final</h3><p>对于 final 域，编译器和处理器要遵守两个重排序规则：</p>
<ol>
<li>在构造函数内对一个 final 域的写入，与随后把这个被构造对象的引用赋值给一个引用变量，这两个操作之间不能重排序。</li>
<li>初次读一个包含 final 域的对象的引用，与随后初次读这个 final 域，这两个操作之间不能重排序。</li>
</ol>
<h4 id="写-final-域的重排序规则"><a href="#写-final-域的重排序规则" class="headerlink" title="写 final 域的重排序规则"></a>写 final 域的重排序规则</h4><p>写 final 域的重排序规则禁止把 final 域的写重排序到构造函数之外。这个规则的实现包含下面2个方面：</p>
<ul>
<li>JMM 禁止编译器把 final 域的写重排序到构造函数之外。</li>
<li>编译器会在 final 域的写之后，构造函数 return 之前，插入一个 StoreStore 屏障。这个屏障禁止处理器把 final 域的写重排序到构造函数之外。</li>
</ul>
<h4 id="读-final-域的重排序规则"><a href="#读-final-域的重排序规则" class="headerlink" title="读 final 域的重排序规则"></a>读 final 域的重排序规则</h4><p>在一个线程中，初次读对象引用与初次读该对象包含的 final 域，JMM 禁止处理器重排序这两个操作（注意，这个规则仅仅针对处理器）。编译器会在读 final 域操作的前面插入一个 LoadLoad 屏障。</p>
<h4 id="final-域是引用类型"><a href="#final-域是引用类型" class="headerlink" title="final 域是引用类型"></a>final 域是引用类型</h4><p>对于引用类型，写 final 域的重排序规则对编译器和处理器增加了如下约束：</p>
<p>在构造函数内对一个 final 引用的对象的成员域的写入，与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量，这两个操作之间不能重排序。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><h4 id="JMM，处理器内存模型与顺序一致性内存模型之间的关系"><a href="#JMM，处理器内存模型与顺序一致性内存模型之间的关系" class="headerlink" title="JMM，处理器内存模型与顺序一致性内存模型之间的关系"></a>JMM，处理器内存模型与顺序一致性内存模型之间的关系</h4><p>JMM 是一个语言级的内存模型，处理器内存模型是硬件级的内存模型，顺序一致性内存模型是一个理论参考模型。下面是语言内存模型，处理器内存模型和顺序一致性内存模型的强弱对比示意图：</p>
<p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7l8rgjej30ys0r6n05.jpg" alt="2018-02-28_15-34-39"></p>
<h4 id="JMM-的设计示意图"><a href="#JMM-的设计示意图" class="headerlink" title="JMM 的设计示意图"></a>JMM 的设计示意图</h4><p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1fow7lb5c5kj310y0z4q7w.jpg" alt="jmm-"></p>
<h4 id="JMM-的内存可见性保证"><a href="#JMM-的内存可见性保证" class="headerlink" title="JMM 的内存可见性保证"></a>JMM 的内存可见性保证</h4><p>Java 程序的内存可见性保证按程序类型可以分为下列三类：</p>
<p>1.单线程程序。单线程程序不会出现内存可见性问题。编译器，runtime 和处理器会共同确保单线程程序的执行结果与该程序在顺序一致性模型中的执行结果相同。</p>
<p>2.正确同步的多线程程序。正确同步的多线程程序的执行将具有顺序一致性（程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同）。这是 JMM 关注的重点，JMM通过限制编译器和处理器的重排序来为程序员提供内存可见性保证。</p>
<p>3.未同步/未正确同步的多线程程序。JMM 为它们提供了最小安全性保障：线程执行时读取到的值，要么是之前某个线程写入的值，要么是默认值（0，null，false）。</p>
<p>下图展示了这三类程序在 JMM 中与在顺序一致性内存模型中的执行结果的异同：</p>
<p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1fow7lgmupij311m0sqtcg.jpg" alt="2018-02-28_15-43-10"></p>
<p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1fp3jkmizmpj30o00didgn.jpg" alt=""></p>

      
      
        <div class="page-reward">
          <p><a href="javascript:void(0)" onclick="dashangToggle()" class="dashang">赏</a></p>
          <div class="hide_box"></div>
          <div class="shang_box">
            <a class="shang_close" href="javascript:void(0)" onclick="dashangToggle()">×</a>
            <div class="shang_tit">
              <p>纯属好玩</p>
            </div>
            <div class="shang_payimg">
              <img src="/img/alipayimg.jpg" alt="扫码支持" title="扫一扫" />
            </div>
              <div class="pay_explain">扫码打赏，你说多少就多少</div>
            <div class="shang_payselect">
              
                <div class="pay_item checked" data-id="alipay">
                  <span class="radiobox"></span>
                  <span class="pay_logo"><img src="/img/alipay.png" alt="支付宝" /></span>
                </div>
              
              
                <div class="pay_item" data-id="wechat">
                  <span class="radiobox"></span>
                  <span class="pay_logo"><img src="/img/weixin.png" alt="微信" /></span>
                </div>
              
            </div>
            <div class="shang_info">
              <p>打开<span id="shang_pay_txt">支付宝</span>扫一扫，即可进行扫码打赏哦</p>
            </div>
          </div>
        </div>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
        <script type="text/javascript">
          $(".pay_item").click(function(){
            $(this).addClass('checked').siblings('.pay_item').removeClass('checked');
            var dataid=$(this).attr('data-id');
            $(".shang_payimg img").attr("src","/img/"+dataid+"img.jpg");
            $("#shang_pay_txt").text(dataid=="alipay"?"支付宝":"微信");
          });
          function dashangToggle(){
            
            $(".hide_box").fadeToggle();
            $(".shang_box").fadeToggle();
          }
        </script>
      
    </div>
    
  </div>
  
    
    <div class="copyright">
        <p><span>本文标题:</span><a href="/2018/02/28/Java-Memory-Model/">《深入理解 Java 内存模型》读书笔记</a></p>
        <p><span>文章作者:</span><a href="/" title="访问 zhisheng 的个人博客">zhisheng</a></p>
        <p><span>发布时间:</span>2018年02月28日 - 00时00分</p>
        <p><span>最后更新:</span>2018年11月22日 - 22时51分</p>
        <p>
            <span>原始链接:</span><a class="post-url" href="/2018/02/28/Java-Memory-Model/" title="《深入理解 Java 内存模型》读书笔记">http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/</a>
            <span class="copy-path" data-clipboard-text="原文: http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/　　作者: zhisheng" title="点击复制文章链接"><i class="fa fa-clipboard"></i></span>
            <script src="/js/clipboard.min.js"></script>
            <script> var clipboard = new Clipboard('.copy-path'); </script>
        </p>
        <p>
            <span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/cn/" title="中国大陆 (CC BY-NC-SA 3.0 CN)" target = "_blank">"署名-非商用-相同方式共享 3.0"</a> 转载请保留原文链接及作者。
        </p>
    </div>



<nav id="article-nav">
  
    <a href="/2018/03/04/springcloud-vedio/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption"><</strong>
      <div class="article-nav-title">
        
          小马哥 《Java 微服务实践 - Spring Cloud 系列》强烈推荐
        
      </div>
    </a>
  
  
    <a href="/2018/02/07/SpringBoot-RocketMQ/" id="article-nav-older" class="article-nav-link-wrap">
      <div class="article-nav-title">Spring Boot系列文章（六）：SpringBoot RocketMQ 整合使用和监控</div>
      <strong class="article-nav-caption">></strong>
    </a>
  
</nav>

  
</article>

    <div id="toc" class="toc-article">
    <strong class="toc-title">文章目录</strong>
    <ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#前提"><span class="toc-number">1.</span> <span class="toc-text">前提</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#关注我"><span class="toc-number">2.</span> <span class="toc-text">关注我</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#基础"><span class="toc-number">3.</span> <span class="toc-text">基础</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#并发编程的模型分类"><span class="toc-number">3.1.</span> <span class="toc-text">并发编程的模型分类</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#通信"><span class="toc-number">3.1.1.</span> <span class="toc-text">通信</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#同步"><span class="toc-number">3.1.2.</span> <span class="toc-text">同步</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Java-内存模型的抽象"><span class="toc-number">3.2.</span> <span class="toc-text">Java 内存模型的抽象</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#重排序"><span class="toc-number">4.</span> <span class="toc-text">重排序</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#处理器重排序"><span class="toc-number">4.1.</span> <span class="toc-text">处理器重排序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#内存屏障指令"><span class="toc-number">4.2.</span> <span class="toc-text">内存屏障指令</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#happens-before"><span class="toc-number">4.3.</span> <span class="toc-text">happens-before</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#数据依赖性"><span class="toc-number">4.4.</span> <span class="toc-text">数据依赖性</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#as-if-serial-语义"><span class="toc-number">4.5.</span> <span class="toc-text">as-if-serial 语义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#重排序对多线程的影响"><span class="toc-number">4.6.</span> <span class="toc-text">重排序对多线程的影响</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#顺序一致性"><span class="toc-number">5.</span> <span class="toc-text">顺序一致性</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#顺序一致性内存模型"><span class="toc-number">5.1.</span> <span class="toc-text">顺序一致性内存模型</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#同步程序的顺序一致性效果"><span class="toc-number">5.2.</span> <span class="toc-text">同步程序的顺序一致性效果</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#未同步程序的执行特性"><span class="toc-number">5.3.</span> <span class="toc-text">未同步程序的执行特性</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Volatile"><span class="toc-number">6.</span> <span class="toc-text">Volatile</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Volatile-特性"><span class="toc-number">6.1.</span> <span class="toc-text">Volatile 特性</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#volatile-写-读的内存定义"><span class="toc-number">6.2.</span> <span class="toc-text">volatile 写-读的内存定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#volatile-内存语义的实现"><span class="toc-number">6.3.</span> <span class="toc-text">volatile 内存语义的实现</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#锁"><span class="toc-number">7.</span> <span class="toc-text">锁</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#锁释放和获取的内存语义"><span class="toc-number">7.1.</span> <span class="toc-text">锁释放和获取的内存语义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#锁内存语义的实现"><span class="toc-number">7.2.</span> <span class="toc-text">锁内存语义的实现</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#concurrent-包的实现"><span class="toc-number">7.3.</span> <span class="toc-text">concurrent 包的实现</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#final"><span class="toc-number">8.</span> <span class="toc-text">final</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#写-final-域的重排序规则"><span class="toc-number">8.1.</span> <span class="toc-text">写 final 域的重排序规则</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#读-final-域的重排序规则"><span class="toc-number">8.2.</span> <span class="toc-text">读 final 域的重排序规则</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#final-域是引用类型"><span class="toc-number">8.3.</span> <span class="toc-text">final 域是引用类型</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#总结"><span class="toc-number">9.</span> <span class="toc-text">总结</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#JMM，处理器内存模型与顺序一致性内存模型之间的关系"><span class="toc-number">9.1.</span> <span class="toc-text">JMM，处理器内存模型与顺序一致性内存模型之间的关系</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JMM-的设计示意图"><span class="toc-number">9.2.</span> <span class="toc-text">JMM 的设计示意图</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JMM-的内存可见性保证"><span class="toc-number">9.3.</span> <span class="toc-text">JMM 的内存可见性保证</span></a></li></ol></li></ol>
</div>
<input type="button" id="tocButton" value="隐藏目录"  title="点击按钮隐藏或者显示文章目录">

<script src="https://7.url.cn/edu/jslib/comb/require-2.1.6,jquery-1.9.1.min.js"></script>
<script>
    var valueHide = "隐藏目录";
    var valueShow = "显示目录";

    if ($(".left-col").is(":hidden")) {
        $("#tocButton").attr("value", valueShow);
    }
    $("#tocButton").click(function() {
        if ($("#toc").is(":hidden")) {
            $("#tocButton").attr("value", valueHide);
            $("#toc").slideDown(320);
        }
        else {
            $("#tocButton").attr("value", valueShow);
            $("#toc").slideUp(350);
        }
    })
    if ($(".toc").length < 1) {
        $("#toc, #tocButton").hide();
    }
</script>





<div class="bdsharebuttonbox">
	<a href="#" class="fx fa-weibo bds_tsina" data-cmd="tsina" title="分享到新浪微博"></a>
	<a href="#" class="fx fa-weixin bds_weixin" data-cmd="weixin" title="分享到微信"></a>
	<a href="#" class="fx fa-qq bds_sqq" data-cmd="sqq" title="分享到QQ好友"></a>
	<a href="#" class="fx fa-facebook-official bds_fbook" data-cmd="fbook" title="分享到Facebook"></a>
	<a href="#" class="fx fa-twitter bds_twi" data-cmd="twi" title="分享到Twitter"></a>
	<a href="#" class="fx fa-linkedin bds_linkedin" data-cmd="linkedin" title="分享到linkedin"></a>
	<a href="#" class="fx fa-files-o bds_copy" data-cmd="copy" title="分享到复制网址"></a>
</div>
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"2","bdSize":"24"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>




    
        <div id="gitments"></div>
<script src="/js/gitment.browser.js"></script>
<script>
    var gitment = new Gitment({
      id: window.location.pathname,
      owner: 'zhisheng',
      repo: 'zhisheng17.github.io',
      oauth: {
        client_id: '',
        client_secret: '',
      },
    })
    gitment.render('gitments')
</script>
    



    <div class="scroll" id="post-nav-button">
        
            <a href="/2018/03/04/springcloud-vedio/" title="上一篇: 小马哥 《Java 微服务实践 - Spring Cloud 系列》强烈推荐">
                <i class="fa fa-angle-left"></i>
            </a>
        
        <a title="文章列表"><i class="fa fa-bars"></i><i class="fa fa-times"></i></a>
        
            <a href="/2018/02/07/SpringBoot-RocketMQ/" title="下一篇: Spring Boot系列文章（六）：SpringBoot RocketMQ 整合使用和监控">
                <i class="fa fa-angle-right"></i>
            </a>
        
    </div>
    <ul class="post-list"><li class="post-list-item"><a class="post-list-link" href="/2019/01/06/Flink-Kafka-sink/">《从0到1学习Flink》—— Flink 写入数据到 Kafka</a></li><li class="post-list-item"><a class="post-list-link" href="/2019/01/05/Flink-run/">《从0到1学习Flink》—— Flink 项目如何运行？</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/12/30/Flink-ElasticSearch-Sink/">《从0到1学习Flink》—— Flink 写入数据到 ElasticSearch</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/12/11/Flink-time/">《从0到1学习Flink》—— Flink 中几种 Time 详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/12/08/Flink-Stream-Windows/">《从0到1学习Flink》—— 介绍Flink中的Stream Windows</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/11/04/Flink-Data-transformation/">《从0到1学习Flink》—— Flink Data transformation(转换)</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/10/31/flink-create-sink/">《从0到1学习Flink》—— 如何自定义 Data Sink ？</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/10/30/flink-create-source/">《从0到1学习Flink》—— 如何自定义 Data Source ？</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/10/29/flink-sink/">《从0到1学习Flink》—— Data Sink 介绍</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/10/28/flink-sources/">《从0到1学习Flink》—— Data Source 介绍</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/10/27/flink-config/">《从0到1学习Flink》—— Flink 配置文件详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/10/13/flink-introduction/">《从0到1学习Flink》—— Apache Flink 介绍</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/09/18/flink-install/">《从0到1学习Flink》—— Mac 上搭建 Flink 1.6.0 环境并构建运行简单程序入门</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/08/30/go-sync/">Go 并发——实现协程同步的几种方式</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/08/14/idea-remote-debug-elasticsearch/">教你如何在 IDEA 远程 Debug ElasticSearch</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/08/12/es-code03/">渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程（下）</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/08/11/es-code02/">渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程（上）</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/08/05/es-code01/">渣渣菜鸡的 ElasticSearch 源码解析 —— 环境搭建</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/08/04/why-see-es-code/">渣渣菜鸡为什么要看 ElasticSearch 源码？</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/07/31/alipay02/">渣渣菜鸡的蚂蚁金服面试经历(二)</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/07/30/alipay01/">渣渣菜鸡的蚂蚁金服面试经历(一)</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/07/12/youzan/">渣渣菜鸡的有赞面试经历（一）</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/06/20/java-8-date/">20 个案例教你在 Java 8 中如何处理日期和时间?</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/06/19/SimpleDateFormat/">SimpleDateFormat 如何安全的使用？</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/05/26/paper/">苦逼的毕业论文经历</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/04/30/springboot_SpringApplication/">Spring Boot 2.0系列文章(七)：SpringApplication 深入探索</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/04/24/Distributed_lock/">分布式锁看这篇就够了</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/04/19/SpringBootApplication-annotation/">Spring Boot 2.0系列文章(六)：Spring Boot 2.0中SpringBootApplication注解详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/04/18/spring_boot2_project/">Spring Boot 2.0系列文章(五)：Spring Boot 2.0 项目源码结构预览</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/04/15/springboot2_code/">Spring Boot 2.0系列文章(四)：Spring Boot 2.0 源码阅读环境搭建</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/04/13/Spring_Boot_2.0_Configuration_Changelog/">Spring Boot 2.0系列文章(三)：Spring Boot 2.0 配置改变</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/03/27/blogs/">写这么多系列博客，怪不得找不到女朋友</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/03/06/SpringBoot2-new-features/">Spring Boot 2.0系列文章(二)：Spring Boot 2.0 新特性详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/03/06/SpringBoot2-Migration-Guide/">Spring Boot 2.0系列文章(一)：Spring Boot 2.0 迁移指南</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/03/04/springboot-vedio/">小马哥 《Java 微服务实践 - Spring Boot 系列》强烈推荐</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/03/04/springcloud-vedio/">小马哥 《Java 微服务实践 - Spring Cloud 系列》强烈推荐</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/02/28/Java-Memory-Model/">《深入理解 Java 内存模型》读书笔记</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/02/07/SpringBoot-RocketMQ/">Spring Boot系列文章（六）：SpringBoot RocketMQ 整合使用和监控</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/02/07/rocketmq-example/">RocketMQ系列文章（三）：RocketMQ 简单的消息示例</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/02/06/RocketMQ-install/">RocketMQ系列文章（二）：RocketMQ 安装及快速入门</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/02/05/RocketMQ/">RocketMQ系列文章（一）：RocketMQ 初探</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/28/RabbitMQ/">Spring Boot系列文章（五）：SpringBoot RabbitMQ 整合进阶版</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/27/SpringBoot-ActiveMQ/">Spring Boot系列文章（四）：SpringBoot ActiveMQ 整合使用</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/26/SpringBoot-RabbitMQ/">Spring Boot系列文章（三）：SpringBoot  RabbitMQ 整合使用</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/25/Docker-install/">Docker系列文章（二）：Mac 安装 Docker 及常用命令</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/24/mac/">MacBook Pro 初体验</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/17/SpringBoot-Admin/">Spring Boot系列文章（二）：SpringBoot Admin 使用指南</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/09/lombok/">Lombok 看这篇就够了</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/05/SpringBoot-Kafka/">Spring Boot系列文章（一）：SpringBoot Kafka 整合使用</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/04/Kafka/">Kafka 安装及快速入门</a></li><li class="post-list-item"><a class="post-list-link" href="/2018/01/04/weixin/">为什么要重新运营以前的公众号呢？</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/27/consul-install/">Windows 下安装 Consul</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/25/ELK/">Elasticsearch 系列文章（五）：ELK 实时日志分析平台环境搭建</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/18/hexo-yilia/">Hexo + yilia 搭建博客可能会遇到的所有疑问</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/13/Google-Developer-Days/">谷歌开发者大会收获满满，不去真 “可惜” 了</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/09/CodeMirror/">使用 CodeMirror 打造属于自己的在线代码编辑器</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/08/netty-01-env/">Netty系列文章（一）：Netty 源码阅读之初始环境搭建</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/03/RestTemplate/">RestTemplate 详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/12/02/wx-01/">实习圈群里提问小记</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/11/26/Docker-harbor/">Docker系列文章（一）：基于 Harbor 搭建 Docker 私有镜像仓库</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/11/18/flow-control/">基于分布式环境下限流系统的设计</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/11/18/Money-management/">谈谈我的理财</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/11/11/recommended-books/">送你一份双十一剁手书单【墙裂推荐】</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/11/11/Maven-dependencies-dependencyManagement/">Maven 中 dependencies 与 dependencyManagement 的区别</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/10/28/Data-Desensitization/">小白谈数据脱敏</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/10/21/HBase-metrics/">HBase 集群监控</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/10/18/ElasticSearch-nodes-metrics/">Elasticsearch 系列文章（四）：ElasticSearch 单个节点监控</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/10/15/ElasticSearch-cluster-health-metrics/">Elasticsearch 系列文章（三）：ElasticSearch 集群监控</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/10/14/Nexus3-Maven/">Centos7 搭建最新 Nexus3 Maven 私服</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/10/14/JsonPath/">JsonPath —— JSON 解析神器</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/09/23/Guava-limit/">Google Guava 缓存实现接口的限流</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/09/17/Interview-summary/">面试过阿里等互联网大公司，我知道了这些套路</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/09/15/linux-lua-lfs-install/">Linux 下 lua 开发环境安装及安装 luafilesystem</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/09/09/Elasticsearch-install/">Elasticsearch 系列文章（二）：全文搜索引擎 Elasticsearch 集群搭建入门教程</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/09/08/Elasticsearch-analyzers/">Elasticsearch 系列文章（一）：Elasticsearch 默认分词器和中分分词器之间的比较及使用方法</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/28/recommend-books/">那些年我看过的书 —— 致敬我的大学生活 —— Say Good Bye ！</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/18/Ubuntu-install-Nginx/">Ubuntu16.10 安装 Nginx</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/11/most-success/">马云热血励志演讲《最伟大的成功》</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/08/android-projects/">源码大招：不服来战！撸这些完整项目，你不牛逼都难！</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/05/Nginx/">Nginx 基本知识快速入门</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/04/alibaba/">秋招第三站 —— 内推阿里（一面）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/04/yaxin/">秋招第一站 —— 亚信科技</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/08/04/iqiyi/">秋招第二站 —— 内推爱奇艺（一面二面）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/29/ThreadPool/">Java 线程池艺术探索</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/25/Java-performance-tuning/">Java 性能调优需要格外注意的细节</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/21/Spring-MVC03/">Spring MVC系列文章（五）：看透 Spring MVC 源代码分析与实践 ——  Spring MVC 组件分析</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/14/Spring-MVC01/">Spring MVC系列文章（三）：看透 Spring MVC 源代码分析与实践 ——  网站基础知识</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/14/Spring-MVC02/">Spring MVC系列文章（四）：看透 Spring MVC 源代码分析与实践 ——  俯视 Spring MVC</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/09/servlet/">通过源码详解 Servlet</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/28/Velocity-foreach/">Velocity 循环指令一种好的解决方法</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/23/java-io/">Java IO流学习超详细总结（图文并茂）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/23/AJAX/">AJAX 学习</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/20/Java-error1/">java.sql.SQLException Field 'id' doesn't have a default value</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/19/中缀表达式转换成前缀和后缀表达式这类题目的超实用解题技巧/">中缀表达式转换成前缀和后缀表达式这类题目的超实用解题技巧</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/18/Bootstrap入门需掌握的知识点（二）/">Bootstrap入门需掌握的知识点（二）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/18/Bootstrap入门需掌握的知识点（一）/">Bootstrap入门需掌握的知识点（一）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/18/循环队列的相关条件和公式/">循环队列的相关条件和公式</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/17/详解 Filter 过滤器/">详解 Filter 过滤器</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/17/blog-talk/">搭建一个博客项目后的碎碎念</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/17/详细深入分析 Java ClassLoader 工作机制/">详细深入分析 Java ClassLoader 工作机制</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/16/通过项目逐步深入了解Spring MVC（一）/">通过项目逐步深入了解Spring MVC（一）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/15/通过项目逐步深入了解Mybatis(四)/">通过项目逐步深入了解Mybatis（四）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/14/通过项目逐步深入了解Mybatis(三)/">通过项目逐步深入了解Mybatis（三）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Hexo-yilia-toc/">Hexo + yilia 主题实现文章目录</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/HashMap-Hashtable/">HashMap、Hashtable、HashSet 和 ConcurrentHashMap 的比较</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Spring MVC+Hibernate JPA搭建的博客系统项目中所遇到的坑/">Spring MVC系列文章（二）：Spring MVC+Hibernate JPA搭建的博客系统项目中所遇到的坑</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Spring MVC + Hibernate JPA + Bootstrap 搭建的博客系统/">Spring MVC系列文章（一）：Spring MVC + Hibernate JPA + Bootstrap 搭建的博客系统 Demo</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Java-Thread/">《Java 多线程编程核心技术》学习笔记及总结</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Python爬虫实战之爬取糗事百科段子/">Python爬虫实战之爬取糗事百科段子</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/通过项目逐步深入了解Mybatis(二)/">通过项目逐步深入了解Mybatis（二）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Pyspider框架 —— Python爬虫实战之爬取 V2EX 网站帖子/">Pyspider框架 —— Python爬虫实战之爬取 V2EX 网站帖子</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/MySQL-select-good/">MySQL 处理海量数据时的一些优化查询速度方法</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/MyBatis-foreach/">MyBatis的foreach语句详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Java连接Oracle数据库的三种连接方式/">Java连接Oracle数据库的三种连接方式</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/【字符串】判断两字符串是否互为旋转词？/">【字符串】判断两字符串是否互为旋转词？</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/【字符串】字符串逆序/">字符串</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/feiji/">记录下自己第一次坐飞机的感受</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/JVM性能调优监控工具jps、jstack、jmap、jhat、jstat等使用详解/">JVM性能调优监控工具jps、jstack、jmap、jhat、jstat等使用详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Java-16-lession/">《疯狂 Java 突破程序员基本功的 16 课》读书笔记</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/奇怪的Java题：为什么128 == 128返回为False，而127 == 127会返回为True-/">奇怪的Java题：为什么128 == 128返回为False，而127 == 127会返回为True?</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/利用Github Page 搭建个人博客网站/">利用Github Page 搭建个人博客网站</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/深入分析 Java Web 中的中文编码问题/">深入分析 Java Web 中的中文编码问题</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/程序访问文件的几种方式/">程序访问文件的几种方式</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/深度探究Java 中 finally 语句块/">深度探究Java 中 finally 语句块</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/解决jdk1.8中发送邮件失败（handshake_failure）问题/">解决jdk1.8中发送邮件失败（handshake_failure）问题</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/java-var/">从对象深入分析 Java 中实例变量和类变量的区别</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/String-new/">关于String s = new String("xyz"); 创建几个对象的问题</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/java读取文件/">Java读取文件</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Java NIO 系列教程/">Java NIO 系列教程</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/13/Python爬虫实战之爬取百度贴吧帖子/">Python爬虫实战之爬取百度贴吧帖子</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/12/通过项目逐步深入了解Mybatis(一)/">通过项目逐步深入了解Mybatis（一）</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/02/poetry2/"> 六月 —— 愿你做最美好的自己！</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/05/12/poetry/">最近很火的鸡汤，分享给大家</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/13/Hexo-yilia-changyan/">Github pages + Hexo 博客 yilia 主题使用畅言评论系统</a></li></ul>
    <script src="https://7.url.cn/edu/jslib/comb/require-2.1.6,jquery-1.9.1.min.js"></script>
    <script>
        $(".post-list").addClass("toc-article");
        $(".post-list-item a").attr("target","_blank");
        $("#post-nav-button > a:nth-child(2)").click(function() {
            $(".fa-bars, .fa-times").toggle();
            $(".post-list").toggle(300);
            if ($(".toc").length > 0) {
                $("#toc, #tocButton").toggle(200, function() {
                    if ($(".switch-area").is(":visible")) {
                        $("#tocButton").attr("value", valueHide);
                        }
                    })
            }
            else {
            }
        })
    </script>



    <script>
        
    </script>
</div>
      <footer id="footer">
    <div class="outer">
        <div id="footer-info">
            <div class="footer-left">
                &copy; 2019 zhisheng
            </div>
        </div>
        
            <div class="visit">
                
                    <span id="busuanzi_container_site_pv" style='display:none'>
                        <span id="site-visit" >本站总访问量: 
                            <span id="busuanzi_value_site_pv"></span>
                        </span>
                    </span>
                
                
                    <span>, </span>
                
                
                    <span id="busuanzi_container_page_pv" style='display:none'>
                        <span id="page-visit">本文总阅读量: 
                            <span id="busuanzi_value_page_pv"></span>
                        </span>
                    </span>
                
            </div>
        
    </div>
</footer>

    </div>
    <script src="https://7.url.cn/edu/jslib/comb/require-2.1.6,jquery-1.9.1.min.js"></script>
<script src="/js/main.js"></script>

    <script>
        $(document).ready(function() {
            var backgroundnum = 24;
            var backgroundimg = "url(/background/bg-x.jpg)".replace(/x/gi, Math.ceil(Math.random() * backgroundnum));
            $("#mobile-nav").css({"background-image": backgroundimg,"background-size": "cover","background-position": "center"});
            $(".left-col").css({"background-image": backgroundimg,"background-size": "cover","background-position": "center"});
        })
    </script>




	<script>
	var _hmt = _hmt || [];
	(function() {
	  var hm = document.createElement("script");
	  hm.src = "https://hm.baidu.com/hm.js?c41be5a0c9f014e977695f66c065b5d3";
	  var s = document.getElementsByTagName("script")[0]; 
	  s.parentNode.insertBefore(hm, s);
	})();
	</script>


<div class="scroll" id="scroll">
    <a href="#"><i class="fa fa-arrow-up"></i></a>
    <a href="#comments"><i class="fa fa-comments-o"></i></a>
    <a href="#footer"><i class="fa fa-arrow-down"></i></a>
</div>
<script>
    $(document).ready(function() {
        if ($("#comments").length < 1) {
            $("#scroll > a:nth-child(2)").hide();
        };
    })
</script>

<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js">
</script>

  <script language="javascript">
    $(function() {
        $("a[title]").each(function() {
            var a = $(this);
            var title = a.attr('title');
            if (title == undefined || title == "") return;
            a.data('title', title).removeAttr('title').hover(

            function() {
                var offset = a.offset();
                $("<div id=\"anchortitlecontainer\"></div>").appendTo($("body")).html(title).css({
                    top: offset.top - a.outerHeight() - 15,
                    left: offset.left + a.outerWidth()/2 + 1
                }).fadeIn(function() {
                    var pop = $(this);
                    setTimeout(function() {
                        pop.remove();
                    }, pop.text().length * 800);
                });
            }, function() {
                $("#anchortitlecontainer").remove();
            });
        });
    });
</script>


  </div>
</body>
</html>